package ly.count.ohos.sdk;

import ohos.aafwk.ability.Ability;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Rating;
import ohos.agp.components.Text;
import ohos.agp.components.webengine.WebAgent;
import ohos.agp.components.webengine.WebView;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.image.PixelMap;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class ModuleRatings extends ModuleBase {
    static final String STAR_RATING_EVENT_KEY = "[CLY]_star_rating";

    private static final int SESSION_MAX = 5;

    // star rating
    StarRatingCallback starRatingCallback; // saved callback that is used for automatic star rating
    boolean isShowStarRatingDialogOnFirstActivity = false;

    final Ratings ratingsInterface;

    ModuleLog moduleLog;
    private final int defaultRating = 5;

    private final float widthProportion = 0.8f;
    private final float heightProportion = 0.25f;

    ModuleRatings(Countly cly, CountlyConfig config) {
        super(cly);

        moduleLog = cly.L;

        moduleLog.v("[ModuleRatings] Initialising");

        starRatingCallback = config.starRatingCallback;
        setStarRatingInitConfig(config.countlyStore, config.starRatingSessionLimit,
            config.starRatingTextTitle, config.starRatingTextMessage, config.starRatingTextDismiss);
        setIfRatingDialogIsCancellableInternal(config.countlyStore, config.starRatingDialogIsCancellable);
        setShowDialogAutomatically(config.countlyStore, config.starRatingShownAutomatically);
        setStarRatingDisableAskingForEachAppVersion(config.countlyStore,
            config.starRatingDisableAskingForEachAppVersion);

        ratingsInterface = new Ratings();
    }

    void recordManualRatingInternal(String widgetId, int rate,
                                    String email, String comment, boolean isUserCanBeContacted) {
        moduleLog.d("[ModuleRatings] Calling recordManualRatingInternal");

        if (!Countly.sharedInstance().getConsent(Countly.CountlyFeatureNames.starRating)) {
            return;
        }
        int rating = rate;
        if (widgetId == null) {
            moduleLog.d("[ModuleRatings] recordManualRatingInternal, provided widget ID is null, returning");
            return;
        }

        if (widgetId.isEmpty()) {
            moduleLog.d("[ModuleRatings] recordManualRatingInternal, provided widget ID is empty, returning");
            return;
        }

        if (rating < 0) {
            rating = 0;

            moduleLog.d("[ModuleRatings] recordManualRatingInternal, given rating too low, defaulting to 0");
        }

        if (rating > defaultRating) {
            rating = defaultRating;

            moduleLog.d("[ModuleRatings] recordManualRatingInternal, given rating too high, defaulting to 5");
        }

        Map<String, Object> segm = new HashMap<>();
        segm.put("platform", "ohos");
        segm.put("app_version", DeviceInfo.getAppVersion(mCly.context_));
        segm.put("rating", "" + rating);
        segm.put("widget_id", widgetId);
        segm.put("contactMe", isUserCanBeContacted);

        if (email != null && !email.isEmpty()) {
            segm.put("email", email);
        }

        if (comment != null && !comment.isEmpty()) {
            segm.put("comment", comment);
        }

        mCly.moduleEvents.recordEventInternal(ModuleRatings.STAR_RATING_EVENT_KEY, segm, 1, 0, 0, null, true);
    }

    /**
     * Save the star rating preferences object
     *
     * @param cs
     * @param srp
     */
    private void saveStarRatingPreferences(final CountlyStore cs, final StarRatingPreferences srp) {
        cs.setStarRatingPreferences(srp.toJson().toString());
    }

    /**
     * Setting things that would be provided during initial config
     *
     * @param cs
     * @param limit limit for automatic rating
     * @param starRatingTextTitle provided title
     * @param starRatingTextMessage provided message
     * @param starRatingTextDismiss provided dismiss text
     */
    void setStarRatingInitConfig(final CountlyStore cs, final int limit, final String starRatingTextTitle,
                                 final String starRatingTextMessage, final String starRatingTextDismiss) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);

        if (limit >= 0) {
            srp.sessionLimit = limit;
        }

        if (starRatingTextTitle != null) {
            srp.dialogTextTitle = starRatingTextTitle;
        }

        if (starRatingTextMessage != null) {
            srp.dialogTextMessage = starRatingTextMessage;
        }

        if (starRatingTextDismiss != null) {
            srp.dialogTextDismiss = starRatingTextDismiss;
        }

        saveStarRatingPreferences(cs, srp);
    }

    /**
     * Set if the star rating dialog should be shown automatically
     *
     * @param cs
     * @param shouldShow
     */
    void setShowDialogAutomatically(final CountlyStore cs, final boolean shouldShow) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        srp.automaticRatingShouldBeShown = shouldShow;
        saveStarRatingPreferences(cs, srp);
    }

    /**
     * 自动显示评级
     *
     * @return boolean类型
     */
    boolean getIfStarRatingShouldBeShownAutomatically() {
        StarRatingPreferences srp = loadStarRatingPreferences(mCly.eventQueue_.getCountlyStore());
        return srp.automaticRatingShouldBeShown;
    }

    /**
     * Set if automatic star rating should be disabled for each new version.
     * By default automatic star rating will be shown for every new app version.
     * If this is set to true, star rating will be shown only once over apps lifetime
     *
     * @param cs
     * @param isDisableAsking if set true, will not show star rating for every new app version
     */
    void setStarRatingDisableAskingForEachAppVersion(final CountlyStore cs, final boolean isDisableAsking) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        srp.isDisabledAutomaticForNewVersions = isDisableAsking;
        saveStarRatingPreferences(cs, srp);
    }

    /**
     * Register that a apps session has transpired.
     * Will increase session counter and show automatic star rating if needed.
     *
     * @param context ohos context
     * @param cs CountlyStore
     * @param starRatingCallback StarRatingCallback
     */
    void registerAppSession(final Context context, final CountlyStore cs, final StarRatingCallback starRatingCallback) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);

        String currentAppVersion = DeviceInfo.getAppVersion(context);

        // a new app version is released, reset all counters
        // if we show the rating once per apps lifetime, don't reset the counters
        if (currentAppVersion != null && !currentAppVersion.equals(srp.appVersion)
            && !srp.isDisabledAutomaticForNewVersions) {
            srp.appVersion = currentAppVersion;
            srp.isShownForCurrentVersion = false;
            srp.sessionAmount = 0;
        }

        srp.sessionAmount++;
        if (srp.sessionAmount >= srp.sessionLimit && !srp.isShownForCurrentVersion
            && srp.automaticRatingShouldBeShown
            && !(srp.isDisabledAutomaticForNewVersions && srp.isAutomaticHasBeenShown)) {
            isShowStarRatingDialogOnFirstActivity = true;
        }

        saveStarRatingPreferences(cs, srp);
    }

    /**
     * Returns the session limit set for automatic star rating
     *
     * @param cs CountlyStore
     * @return int类型
     */
    static int getAutomaticStarRatingSessionLimitInternal(final CountlyStore cs) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        return srp.sessionLimit;
    }

    /**
     * Returns how many sessions has star rating counted internally
     *
     * @param cs CountlyStore
     * @return int类型sessionAmount
     */
    int getCurrentVersionsSessionCountInternal(final CountlyStore cs) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        return srp.sessionAmount;
    }

    /**
     * Set the automatic star rating session count back to 0
     *
     * @param cs CountlyStore
     */
    void clearAutomaticStarRatingSessionCountInternal(final CountlyStore cs) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        srp.sessionAmount = 0;
        saveStarRatingPreferences(cs, srp);
    }

    /**
     * Set if the star rating dialog is cancellable
     *
     * @param cs CountlyStore
     * @param isCancellable
     */
    void setIfRatingDialogIsCancellableInternal(final CountlyStore cs, final boolean isCancellable) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        srp.isDialogCancellable = isCancellable;
        saveStarRatingPreferences(cs, srp);
    }

    /**
     * Class that handles star rating internal state
     *
     * @since 2021-04-22
     */
    static class StarRatingPreferences {
        private static final String KEY_APP_VERSION = "sr_app_version";
        private static final String KEY_SESSION_LIMIT = "sr_session_limit";
        private static final String KEY_SESSION_AMOUNT = "sr_session_amount";
        private static final String KEY_IS_SHOWN_FOR_CURRENT = "sr_is_shown";
        private static final String KEY_AUTOMATIC_RATING_IS_SHOWN = "sr_is_automatic_shown";
        private static final String KEY_DISABLE_AUTOMATIC_NEW_VERSIONS = "sr_is_disable_automatic_new";
        private static final String KEY_AUTOMATIC_HAS_BEEN_SHOWN = "sr_automatic_has_been_shown";
        private static final String KEY_DIALOG_IS_CANCELLABLE = "sr_automatic_dialog_is_cancellable";
        private static final String KEY_DIALOG_TEXT_TITLE = "sr_text_title";
        private static final String KEY_DIALOG_TEXT_MESSAGE = "sr_text_message";
        private static final String KEY_DIALOG_TEXT_DISMISS = "sr_text_dismiss";

        String appVersion = ""; // the name of the current version that we keep track of
        int sessionLimit = SESSION_MAX; // session limit for the automatic star rating
        int sessionAmount = 0; // session amount for the current version
        boolean isShownForCurrentVersion = false; // if automatic star rating has been shown for the current version
        boolean automaticRatingShouldBeShown = false; // if the automatic star rating should be shown
        // if the automatic star star should not be shown for every new apps version
        boolean isDisabledAutomaticForNewVersions = false;
        boolean isAutomaticHasBeenShown = false; // if automatic star rating has been shown for any app's version
        boolean isDialogCancellable = true; // if star rating dialog is cancellable
        String dialogTextTitle = "App rating";
        String dialogTextMessage = "Please rate this app";
        String dialogTextDismiss = "Cancel";

        /**
         * Create a JSONObject from the current state
         *
         * @return JSONObject
         *
         */
        JSONObject toJson() {
            final JSONObject json = new JSONObject();
            try {
                json.put(KEY_APP_VERSION, appVersion);
                json.put(KEY_SESSION_LIMIT, sessionLimit);
                json.put(KEY_SESSION_AMOUNT, sessionAmount);
                json.put(KEY_IS_SHOWN_FOR_CURRENT, isShownForCurrentVersion);
                json.put(KEY_AUTOMATIC_RATING_IS_SHOWN, automaticRatingShouldBeShown);
                json.put(KEY_DISABLE_AUTOMATIC_NEW_VERSIONS, isDisabledAutomaticForNewVersions);
                json.put(KEY_AUTOMATIC_HAS_BEEN_SHOWN, isAutomaticHasBeenShown);
                json.put(KEY_DIALOG_IS_CANCELLABLE, isDialogCancellable);
                json.put(KEY_DIALOG_TEXT_TITLE, dialogTextTitle);
                json.put(KEY_DIALOG_TEXT_MESSAGE, dialogTextMessage);
                json.put(KEY_DIALOG_TEXT_DISMISS, dialogTextDismiss);
            } catch (JSONException e) {
                Countly.sharedInstance().L.w("Got exception converting an StarRatingPreferences to JSON", e);
            }

            return json;
        }

        /**
         * Load the preference state from a JSONObject
         *
         * @param json
         * @return StarRatingPreferences
         *
         */
        static StarRatingPreferences fromJson(final JSONObject json) {
            StarRatingPreferences srp = new StarRatingPreferences();

            if (json != null) {
                try {
                    srp.appVersion = json.getString(KEY_APP_VERSION);
                    srp.sessionLimit = json.optInt(KEY_SESSION_LIMIT, SESSION_MAX);
                    srp.sessionAmount = json.optInt(KEY_SESSION_AMOUNT, 0);
                    srp.isShownForCurrentVersion = json.optBoolean(KEY_IS_SHOWN_FOR_CURRENT, false);
                    srp.automaticRatingShouldBeShown = json.optBoolean(KEY_AUTOMATIC_RATING_IS_SHOWN, true);
                    srp.isDisabledAutomaticForNewVersions = json.optBoolean(KEY_DISABLE_AUTOMATIC_NEW_VERSIONS, false);
                    srp.isAutomaticHasBeenShown = json.optBoolean(KEY_AUTOMATIC_HAS_BEEN_SHOWN, false);
                    srp.isDialogCancellable = json.optBoolean(KEY_DIALOG_IS_CANCELLABLE, true);

                    if (!json.isNull(KEY_DIALOG_TEXT_TITLE)) {
                        srp.dialogTextTitle = json.getString(KEY_DIALOG_TEXT_TITLE);
                    }

                    if (!json.isNull(KEY_DIALOG_TEXT_MESSAGE)) {
                        srp.dialogTextMessage = json.getString(KEY_DIALOG_TEXT_MESSAGE);
                    }

                    if (!json.isNull(KEY_DIALOG_TEXT_DISMISS)) {
                        srp.dialogTextDismiss = json.getString(KEY_DIALOG_TEXT_DISMISS);
                    }
                } catch (JSONException e) {
                    Countly.sharedInstance().L.w("Got exception converting JSON to a StarRatingPreferences", e);
                }
            }

            return srp;
        }
    }

    /**
     * Call to manually show star rating dialog
     *
     * @param context
     * @param cs
     * @param callback
     */
    void showStarRatingInternal(final Context context, final CountlyStore cs, final StarRatingCallback callback) {
        StarRatingPreferences srp = loadStarRatingPreferences(cs);
        showStarRatingCustom(context, srp.dialogTextTitle, srp.dialogTextMessage,
            srp.dialogTextDismiss, srp.isDialogCancellable, callback);
    }

    /**
     * Returns a object with the loaded preferences
     *
     * @param cs
     * @return StarRatingPreferences
     *
     */
    static StarRatingPreferences loadStarRatingPreferences(final CountlyStore cs) {
        String srpString = cs.getStarRatingPreferences();
        StarRatingPreferences srp;

        if (!srpString.equals("")) {
            JSONObject srJson;
            try {
                srJson = new JSONObject(srpString);
                srp = StarRatingPreferences.fromJson(srJson);
            } catch (JSONException e) {
                srp = new StarRatingPreferences();
            }
        } else {
            srp = new StarRatingPreferences();
        }
        return srp;
    }

    /**
     * Method that created the star rating dialog
     *
     * @param context ohos context
     * @param title
     * @param message
     * @param cancelText
     * @param isCancellable
     * @param callback
     */
    void showStarRatingCustom(final Context context, final String title,
                              final String message, final String cancelText,
                              final boolean isCancellable, final StarRatingCallback callback) {
        if (!(context instanceof Ability)) {
            moduleLog.e("[ModuleRatings] Can't show star rating dialog, "
                + "the provided context is not based off a activity");
            return;
        }

        Component dialogComponent = LayoutScatter.getInstance(context)
            .parse(ResourceTable.Layout_star_rating_layout, null, false);
        Button closeButton = (Button) dialogComponent.findComponentById(ResourceTable.Id_star_rating_close);
        Text starTitle = (Text) dialogComponent.findComponentById(ResourceTable.Id_star_title);
        Text starSub = (Text) dialogComponent.findComponentById(ResourceTable.Id_star_sub);
        starTitle.setText(title);
        starSub.setText(message);
        closeButton.setText(cancelText);
        CommonDialog commonDialog = new CommonDialog(context);
        DisplayAttributes realAttributes = DisplayManager.getInstance()
            .getDefaultDisplay(context).get().getRealAttributes();
        commonDialog.setSize((int) (realAttributes.width * widthProportion),
            (int) (realAttributes.height * heightProportion));
        commonDialog.siteRemovable(isCancellable);
        commonDialog.setContentCustomComponent(dialogComponent);

        closeButton.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                commonDialog.destroy();
                if (callback != null) {
                    callback.onDismiss();
                }
            }
        });

        commonDialog.show();
        ModuleRatingBar rating = (ModuleRatingBar) dialogComponent.findComponentById(ResourceTable.Id_ratingBar);
        rating.setRatingChangedListener(new Rating.RatingChangedListener() {
            @Override
            public void onProgressChanged(Rating rating, int i, boolean b) {

            }

            @Override
            public void onStartTrackingTouch(Rating rating) {

            }

            @Override
            public void onStopTrackingTouch(Rating rating) {
                int score = (int) rating.getScore();
                if (Countly.sharedInstance().getConsent(Countly.CountlyFeatureNames.starRating)) {

                    Map<String, String> segm = new HashMap<>();
                    segm.put("platform", "ohos");
                    segm.put("app_version", DeviceInfo.getAppVersion(context));
                    segm.put("rating", "" + score);
                    Countly.sharedInstance().recordEvent(ModuleRatings.STAR_RATING_EVENT_KEY, segm, 1);
                }

                commonDialog.destroy();
                if (callback != null) {
                    callback.onRate(score);
                }
            }
        });
        /*rating.setRatingChangeListener(new ModuleRatingBar.RatingChangeListener() {
            @Override
            public void onRatingChange(int index) {
                int rate = index;

                if (Countly.sharedInstance().getConsent(Countly.CountlyFeatureNames.starRating)) {

                    Map<String, String> segm = new HashMap<>();
                    segm.put("platform", "ohos");
                    segm.put("app_version", DeviceInfo.getAppVersion(context));
                    segm.put("rating", "" + rate);
                    Countly.sharedInstance().recordEvent(ModuleRatings.STAR_RATING_EVENT_KEY, segm, 1);
                }

                commonDialog.destroy();
                if (callback != null) {
                    callback.onRate(rate);
                }
            }
        });*/
    }

    // Countly webDialog user rating

    synchronized void showFeedbackPopupInternal(final String widgetId, final String closeButtonText,
                                                final Ability ability, final FeedbackRatingCallback devCallback) {
        moduleLog.d("[ModuleRatings] Showing Feedback popup for widget id: [" + widgetId + "]");

        if (widgetId == null || widgetId.isEmpty()) {
            if (devCallback != null) {
                devCallback.callback("Countly widgetId cannot be null or empty");
            }
            moduleLog.e("[ModuleRatings] Countly widgetId cannot be null or empty");
            return;
        }

        if (ability == null) {
            if (devCallback != null) {
                devCallback.callback("When showing feedback popup, Activity can't be null");
            }
            moduleLog.e("[ModuleRatings] When showing feedback popup, Activity can't be null");
            return;
        }

        if (!mCly.getConsent(Countly.CountlyFeatureNames.starRating)) {
            if (devCallback != null) {
                devCallback.callback("Consent is not granted");
            }
            return;
        }

        // check the device type
        final boolean isDevicePhone;
        final boolean isDeviceTablet;
        final boolean isDeviceTv;

        isDeviceTv = Utils.isDeviceTv(ability);

        if (!isDeviceTv) {
            isDevicePhone = !Utils.isDeviceTablet(ability);
            isDeviceTablet = Utils.isDeviceTablet(ability);
        } else {
            isDeviceTablet = false;
            isDevicePhone = false;
        }

        String requestData = mCly.connectionQueue_.prepareRatingWidgetRequest(widgetId);
        final String ratingWidgetUrl = mCly.connectionQueue_.getServerURL() + "/feedback?widget_id=" + widgetId
            + "&device_id=" + UtilsNetworking.urlEncodeString(mCly.connectionQueue_.getDeviceId().getId())
            + "&app_key=" + UtilsNetworking.urlEncodeString(mCly.connectionQueue_.getAppKey());

        moduleLog.d("[ModuleRatings] rating widget url :[" + ratingWidgetUrl + "]");

        ConnectionProcessor cp = mCly.connectionQueue_.createConnectionProcessor();

        (new ImmediateRequestMaker()).execute(requestData, "/o/feedback/widget", cp, false,
            new ImmediateRequestMaker.InternalFeedbackRatingCallback() {
                @Override
                public void callback(JSONObject checkResponse) {
                    if (checkResponse == null) {
                        moduleLog.d("[ModuleRatings] Not possible to show Feedback popup for widget id: "
                            + "[" + widgetId + "], probably a lack of connection to the server");
                        if (devCallback != null) {
                            devCallback.callback("Not possible to show Rating popup, "
                                + "probably no internet connection");
                        }
                        return;
                    }

                    if (!checkResponse.has("target_devices")) {
                        moduleLog.d("[ModuleRatings] Not possible to show Feedback popup for widget id: "
                            + "[" + widgetId + "], "
                            + "probably using a widget_id not intended for the rating widget");
                        if (devCallback != null) {
                            devCallback.callback("Not possible to show Rating popup, "
                                + "probably using a widget_id not intended for the rating widget");
                        }
                        return;
                    }

                    try {
                        JSONObject jsonDevices = checkResponse.getJSONObject("target_devices");

                        boolean isShowOnTv = jsonDevices.optBoolean("desktop", false);
                        boolean isShowOnPhone = jsonDevices.optBoolean("phone", false);
                        boolean isShowOnTablet = jsonDevices.optBoolean("tablet", false);

                        if ((isDevicePhone && isShowOnPhone)
                            || (isDeviceTablet && isShowOnTablet) || (isShowOnTv && isShowOnTv)) {
                            // it's possible to show the rating window on this device
                            moduleLog.d("[ModuleRatings] Showing Feedback popup for widget id: [" + widgetId + "]");
                            EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());
                            eventHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    RatingDialogWebView webView = new RatingDialogWebView(ability);
                                    webView.getWebConfig().setJavaScriptPermit(true);
                                    webView.load(ratingWidgetUrl);
                                    CommonDialog commonDialog = new CommonDialog(mCly.context_);
                                    commonDialog.setContentCustomComponent(webView);
                                    if (closeButtonText != null && !closeButtonText.isEmpty()) {
                                        commonDialog.setButton(0, closeButtonText, null);
                                    }
                                    commonDialog.show();
                                }
                            });
                        } else {
                            if (devCallback != null) {
                                devCallback.callback("Rating dialog is not meant for this form factor");
                            }
                        }
                    } catch (JSONException e) {
                        moduleLog.e("[ModuleRatings] Encountered a issue "
                            + "while trying to parse the results of the widget config", e);
                    }
                }
            }, mCly.L);
    }

    static class RatingDialogWebView extends WebView {
        RatingDialogWebView(Context context) {
            super(context);
        }
    }

    static class FeedbackDialogWebAgent extends WebAgent {
        @Override
        public void onLoadingPage(WebView webView, String url, PixelMap icon) {
            Countly.sharedInstance().L.i("attempting to load resource: " + url);
            super.onLoadingPage(webView, url, icon);
        }

        @Override
        public void onLoadingContent(WebView webView, String url) {
            Countly.sharedInstance().L.i("attempting to load resource: " + url);
            super.onLoadingContent(webView, url);
        }
    }

    @Override
    void callbackOnAbilityActive(Ability ability) {
        if (isShowStarRatingDialogOnFirstActivity) {
            CountlyStore cs = mCly.connectionQueue_.getCountlyStore();
            StarRatingPreferences srp = loadStarRatingPreferences(cs);
            srp.isShownForCurrentVersion = true;
            srp.isAutomaticHasBeenShown = true;

            showStarRatingInternal(ability, cs, starRatingCallback);

            saveStarRatingPreferences(cs, srp);
            isShowStarRatingDialogOnFirstActivity = false;
        }
    }

    @Override
    void initFinished(CountlyConfig config) {
        // do star rating related things
        if (mCly.getConsent(Countly.CountlyFeatureNames.starRating)) {
            registerAppSession(config.context, config.countlyStore, starRatingCallback);
        }
    }

    @Override
    void halt() {
    }

    /**
     * 评级
     *
     * @since 2021-04-22
     */
    public class Ratings {
        /**
         * Record user rating manually without showing any message dialog.
         *
         * @param widgetId widget ID to which this rating will be tied. You get it from the dashboard
         * @param rating value from 0 to 5 that will be set as the rating value
         * @param email email of the user
         * @param comment comment set by the user
         * @param isUserCanBeContacted set true if the user wants you to contact him
         */
        public void recordManualRating(String widgetId, int rating, String email,
                                       String comment, boolean isUserCanBeContacted) {
            synchronized (mCly) {
                moduleLog.i("[Ratings] Calling recordManualRating");

                if (widgetId == null || widgetId.isEmpty()) {
                    throw new IllegalStateException("A valid widgetID must be provided. "
                        + "The current one is either null or empty");
                }

                recordManualRatingInternal(widgetId, rating, email, comment, isUserCanBeContacted);
            }
        }

        /**
         * Show the rating dialog to the user
         *
         * @param widgetId ID that identifies this dialog
         * @param closeButtonText
         * @param ability
         * @param callback
         */
        public void showFeedbackPopup(final String widgetId, final String closeButtonText,
                                      final Ability ability, final FeedbackRatingCallback callback) {
            synchronized (mCly) {
                moduleLog.i("[Ratings] Calling showFeedbackPopup");

                showFeedbackPopupInternal(widgetId, closeButtonText, ability, callback);
            }
        }

        /**
         * Shows the star rating dialog
         *
         * @param activity the activity that will own the dialog
         * @param callback callback for the star rating dialog "rate" and "dismiss" events
         */
        public void showStarRating(Ability activity, StarRatingCallback callback) {
            synchronized (mCly) {
                moduleLog.i("[Ratings] Calling showStarRating");

                if (!mCly.getConsent(Countly.CountlyFeatureNames.starRating)) {
                    return;
                }

                showStarRatingInternal(activity, mCly.connectionQueue_.getCountlyStore(), callback);
            }
        }

        /**
         * Returns how many sessions has star rating counted internally for the current apps version
         *
         * @return （int）sessionCount
         */
        public int getCurrentVersionsSessionCount() {
            synchronized (mCly) {
                int sessionCount = getCurrentVersionsSessionCountInternal(mCly.connectionQueue_.getCountlyStore());

                moduleLog.i("[Ratings] Getting star rating current version session count: [" + sessionCount + "]");

                return sessionCount;
            }
        }

        /**
         * Set the automatic star rating session count back to 0
         */
        public void clearAutomaticStarRatingSessionCount() {
            synchronized (mCly) {
                moduleLog.i("[Ratings] Clearing star rating session count");

                clearAutomaticStarRatingSessionCountInternal(mCly.connectionQueue_.getCountlyStore());
            }
        }

        /**
         * Returns the session limit set for automatic star rating
         *
         * @return int
         */
        public int getAutomaticStarRatingSessionLimit() {
            synchronized (mCly) {
                int sessionLimit = ModuleRatings
                    .getAutomaticStarRatingSessionLimitInternal(mCly.connectionQueue_.getCountlyStore());

                moduleLog.i("[Ratings] Getting automatic star rating session limit: [" + sessionLimit + "]");

                return sessionLimit;
            }
        }
    }
}
